#ifndef __ANTICAPTCHA_HPP__
#define __ANTICAPTCHA_HPP__

/**
 *  This file is part of PennerBot
 *
 *  Copyright (C) 2010 microkernel
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string>
#include <vector>
#include <setjmp.h>

#include <boost/thread/recursive_mutex.hpp>

extern "C" {
#include <jpeglib.h>
}

#include <core/random.hpp>

namespace PBot
{
namespace Core
{

/* JPEG stuff */

struct ac_error_mgr
{
    struct jpeg_error_mgr pub;
    jmp_buf setjmp_buffer;
};

typedef struct ac_error_mgr* ac_error_ptr;

void ac_error_exit(j_common_ptr cinfo);
void ac_output_message(j_common_ptr cinfo);

/* Constants */

const int numDigits = 7;

const int lenDigits[] = {38,28,36,28,39,28,33};

const int digits[][48][2] = {
/* 5 */ {{0,0},{1,-6},{0,3},{0,2},{0,2},{1,-11},{0,2},{0,4},{0,7},{1,-15},{1,17},{1,-17},{0,35},{1,-36},{0,18},{1,1},{0,16},{2,-35},{0,20},{0,16},{1,-35},{1,18},{1,16},{1,-35},{0,20},{1,16},{1,-35},{1,19},{0,15},{1,-33},{0,20},{1,12},{1,-12},{1,2},{0,9},{2,-6},{0,2},{0,2}},
/* 9 */ {{0,0},{0,3},{1,-6},{1,8},{2,-10},{0,12},{3,-13},{0,14},{1,13},{2,-28},{0,16},{1,11},{1,-2},{1,-25},{0,16},{1,6},{1,-6},{0,4},{0,3},{1,-24},{1,20},{1,-3},{1,-16},{1,14},{1,-12},{0,9},{1,-7},{1,3}},
/* 8 */ {{0,0},{0,3},{1,-6},{0,8},{1,-21},{1,-3},{0,14},{0,12},{2,-27},{0,13},{0,16},{2,-31},{1,14},{0,17},{2,-30},{1,13},{0,17},{1,-31},{2,13},{0,17},{1,-30},{1,14},{1,-12},{0,27},{1,-14},{1,-11},{0,9},{0,4},{0,11},{1,-9},{1,-13},{0,4},{0,13},{0,3},{1,-18},{0,13}},
/* 6 */ {{0,0},{0,3},{0,6},{0,3},{0,2},{0,3},{0,2},{1,-22},{0,9},{0,16},{2,-27},{0,29},{1,-18},{2,0},{0,18},{3,-19},{0,19},{3,-1},{1,-18},{1,16},{2,-15},{0,14},{2,-13},{0,11},{1,-9},{0,7},{1,-5},{0,3}},
/* 4 */ {{0,0},{0,3},{0,2},{0,2},{1,-11},{0,2},{0,10},{1,-14},{0,15},{1,-13},{1,13},{2,0},{0,2},{2,-2},{0,2},{2,-2},{0,2},{2,-2},{1,2},{1,-4},{0,2},{0,4},{0,2},{0,2},{0,2},{1,-17},{0,2},{0,7},{0,10},{0,2},{1,-26},{0,3},{0,3},{0,2},{0,2},{1,-16},{0,2},{0,2},{0,3}},
/* 7 */ {{0,0},{3,0},{0,15},{2,0},{1,-15},{1,15},{2,-15},{0,14},{2,-15},{0,15},{0,9},{0,3},{0,3},{1,-12},{0,3},{0,5},{1,-26},{0,4},{0,5},{0,4},{0,3},{1,-10},{0,5},{1,-11},{0,3},{0,12},{2,0},{2,0}},
/* 2 */ {{0,0},{0,3},{1,-5},{0,7},{1,-29},{0,26},{1,-6},{0,10},{0,2},{1,-31},{0,17},{1,-19},{0,33},{1,-15},{1,-16},{0,14},{0,18},{2,-34},{0,16},{0,17},{1,-31},{0,12},{1,20},{1,-32},{0,12},{1,-2},{0,21},{1,-29},{0,5},{1,-3},{0,5},{0,23},{1,-26}}
};

const float cos[11] = {0.906307787f, 0.939692621f, 0.965925826f, 0.984807753f, 0.996194698f, 1.0f, 0.996194698f, 0.984807753f, 0.965925826f, 0.939692621f, 0.906307787f};
const float sin[11] = {-0.422618262f, -0.342020143f, -0.258819045f, -0.173648178f, -0.087155743f, 0.0f, 0.087155743f, 0.173648178f, 0.258819045f, 0.342020143f, 0.422618262f};

struct ImageBuffer
{
    unsigned char pixels[200][205][3];
};

class AntiCaptcha
{
private:
    AntiCaptcha();
    AntiCaptcha(const AntiCaptcha&);
    ~AntiCaptcha();

public:
    static AntiCaptcha& GetInstance();
    static void Destroy();

    enum Status
    {
        IOERROR,
        CRACKFAIL,
        CRACKOKAY
    };

    Status Crack(const std::string& filename, int& x, int& y);

private:
    bool LoadFromFile(const std::string& filename);
    bool Analyse();

    //void SaveToFile(const std::string& filename);

    static AntiCaptcha* instance;

    std::vector<std::vector<std::vector<std::pair<unsigned int,unsigned int> > > > transformed;

    unsigned char* buffer;
    ImageBuffer *image;

    unsigned int height;
    unsigned int width;

    unsigned int xout;
    unsigned int yout;

    boost::recursive_mutex ac_mutex;
    Random random;
};

}
}


#endif // __ANTICAPTCHA_HPP__
